共计 8165 个字符,预计需要花费 21 分钟才能阅读完成。
引入
正则表达式是匹配字符串的强大工具, 在每个编程语言中都有正则表达式的概念, 利用正则表达式, 我们从返回的页面内容中提取想要的内容就变的比较方便了
1. 什么是 re 模块
- re 模块是 Python 内置的模块
2. 什么是正则
- 正则表达式是对字符串操作的一种 逻辑 (规则), 就是事先定义好一些特定的字符组合, 组成一个 "规则字符串 ", 然后使用这个 " 规则字符串" 来对字符串进行过滤
- 在 Python 中通过内置的 re 模块来使用正则表达式, 它提供了所有正则表达式的功能
3. 正则表达式的大致匹配过程
- 定义一个"规则字符串", 然后依次与文本中的字符进行对比
- 如果 每一个字符 都能匹配, 那么就匹配成功, 否则匹配失败
- 如果表达式中有 量词 或者 边界, 这个过程会稍微有一些不同
4. 正则小示例
- 比如我们描述四条腿的东西 (规则字符串), 我们可能会想到的有四条腿的动物, 桌子和椅子等等
- 比如我们描述 两条腿的东西并且是活的, 我们可能会想到人类, 猴子, 鸟, 袋鼠, 鸡么等等
5. 常用正则表达式表
模式 | 描述 |
---|---|
\w | 匹配字母数字及下划线 |
\W | 匹配非字母数字下划线 |
\s | 匹配任意空白字符,等价于[\t,\n,\r,\f] |
\S | 匹配任意非空字符 |
\d | 匹配任意数字,等价于[0-9] |
\D | 匹配任意非数字 |
\A | 匹配以 xx 字符串开始 |
\Z | 匹配 xx 字符串结束,如果是存在换行,只匹配到换行前的结束字符串 |
\z | 匹配 xx 字符串结束 |
\G | 匹配最后匹配完成的位置 |
\n | 匹配一个换行符 |
\t | 匹配一个制表符 |
^ | 匹配字符串的开头 |
$ | 匹配字符串的末尾 |
. | 匹配任意字符, 除了换行符, 当 re.DOTALL 标记被指定时, 则可以匹配包括换行符的任意字符 |
[...] | 用来表示一组字符,单独列出:[amk] 匹配 'a','m' 或 'k' |
[^...] | 不在 [] 中的字符:[^abc] 匹配除了 a,b,c 之外的字符 |
* | 匹配 0 个或多个的表达式 |
+ | 匹配 1 个或多个的表达式 |
? | 匹配 0 个或 1 个由前面的正则表达式定义的片段,非贪婪方式 |
{n} | 精确匹配 n 个前面表达式。 |
{n,m} | 匹配 n 到 m 次由前面的正则表达式定义的片段,贪婪方式 |
alb | 匹配 a 或 b |
() | 匹配括号内的表达式,也表示一个组 |
ps : [...]
讲解 : []
所有的特殊字符在字符集中都失去其原有的特殊含义, 在字符集中如果要使用 ]
、-
或^
,可以在前面加上 反斜杠 ,或把]
、-
放在第一个字符,把 ^
放在非第一个字符
更多正则表达式 : https://www.jb51.net/tools/regexsc.htm
一. 普通字符和元字符
正则表达式就是由一些普通字符和元字符组成的
- 普通字符就是 字母和数字
- 元字符就是具有 特殊意义的字符, 常用于表单验证
二. 正则边界(定位字符)
^
: 会匹配字符串或者行的 起始 位置, 在 [] 中表示 ''非''(!)$
: 会匹配字符串或行的 结束 位置 (在 \n 之前)^
,$
一起则表示 精确匹配 包裹在两个符号之间的字符串\A
: 指定匹配必须出现在字符串的开头(忽略 Multiline 选项)\Z
: 指定匹配必须出现在字符串的结尾或字符串结尾的 \n 之前(忽略 Multiline 选项)
三. 量词
-
✳
: 重复零次或更多(>=0) -
✚
: 重复一次或更多次(>=1) -
❔
: 重复零次或一次(0 || 1) -
{}
: 重复多少次的意思 可以有多少个
四. 常用的功能函数
函数 | 说明 |
---|---|
re.match() | 从字符串的起始位置匹配, 匹配成功, 返回一个匹配的对象, 否则返回 None |
re.search() | 扫描整个字符串并返回第一个成功的匹配 |
re.findall() | 在字符串中找到正则表达式所匹配的所有子串, 并返回一个列表, 如果没有找到匹配的, 则返回空列表 |
re.split() | 将一个字符串按照正则表达式匹配结果进行分割, 返回列表类型 |
re.finditer() | 在字符串中找到正则表达式所匹配的所有子串, 并把它们作为一个迭代器返回 |
re.sub() | 把字符串中所有匹配正则表达式的地方替换成新的字符串 |
re.complie() | 将正则表达式传入, 返回一个匹配对象, 一般与其他方法组合使用 |
五. 正则使用示例
💠一对一匹配
print("hello".replace("llo","ooo")) # heooo
print("hello".find("abcd")) # -1 (找不到返回 -1)
print("hello".find("he")) # 0
💠正则匹配
\w
和\W
import re
print(re.findall("\w","ab* 12$ _")) # ['a', 'b', '1', '2', '_']
print(re.findall("\W","ab* 12$ _")) # ['*', '','$',' ']
\s
和\S
print(re.findall("\s","ab* 12$ _ ")) # ['',' ',' ']
print(re.findall("\S","ab* 12$ _ ")) # ['a', 'b', '*', '1', '2', '$', '_']
\d
和\D
print(re.findall("\d","ab* 12$ _")) # ['1', '2']
print(re.findall("\D","ab* 12$ _")) # ['a', 'b', '*', '','$',' ','_']
\A
和\Z
print(re.findall("\Aab","ab* 12$ _")) # ['ab']
print(re.findall("\Aaa","ab* 12$ _")) # [] 没匹配到为空
print(re.findall("_\Z","ab* 12$ _")) # ['_']
print(re.findall("0\Z","ab* 12$ _")) # [] 没匹配到为空
\t
和\n
print(re.findall("\t","ab* 12$ \t_")) # ['\t']
print(re.findall("\n","ab* 12$ \n_")) # ['\n']
🔰"\s" 可以匹配 "\t" 和 "\n"
print(re.findall("\s","ab* \t12$ _\n")) # ['','\t',' ','\n']
^
和\$
print(re.findall("^ab","ab* 12$ _")) # ['ab']
print(re.findall("_$","ab* 12$ _\n")) # ['_']
💠重复匹配
▪
: 匹配一个任意字符
print(re.findall("a.b","a&b")) # ['a&b']
print(re.findall("a.b","a2b")) # ['a2b']
print(re.findall("a.b","acb")) # ['acb']
print(re.findall("a.b","a b")) # ['a b']
print(re.findall("a.b","a\tb")) # ['\t']
print(re.findall("a.b","a\nb")) # [] (换行符匹配不到, 匹配为空)
print(re.findall("a.b","a b a*b abb a_b")) # ['a b', 'a*b', 'abb', 'a_b']
print(re.findall("a.b","a\nb",re.S)) # ['a\nb'] (加入参数, 包含换行)
print(re.findall("a.b","a\nb",re.DOTALL)) # ['a\nb'] (同上效果一样)
✳
: 匹配零个或多个
print(re.findall("a*","aaaa aa")) # ['aaaa', '','aa',''] (零个或多个 a)
print(re.findall("ab*","abab aa")) # ['ab', 'ab', 'a', 'a'] (一个 a 零个或多个 b)
print(re.findall("a*b","ababaaaba aa")) # ['ab', 'ab', 'aaab'] (零个或多个 a 一个 b)
print(re.findall("ab*","bbbbbbbb")) # [] (没有匹配到一个 a 零个或多个 b)
❔
: 匹配零个或一个
print(re.findall("ab?","a")) # ['a']
print(re.findall("ab?","abbb")) # ['ab']
✚
: 匹配一个或多个
print(re.findall("a+","bbb")) # []
print(re.findall("a+","ab")) # ['a']
print(re.findall("ab+","ab")) # ['ab']
print(re.findall("ab+","abbb")) # ['abbb']
{n,m}
: 匹配 n~m 之间个
print(re.findall("a{9}","aaaa")) # [] (匹配 9 个 b, 没有)
print(re.findall("a{3}","aaaa")) # ['aaa']
print(re.findall("a{2}","aaaa")) # ['aa', 'aa']
print(re.findall("ab{2}","aabbb")) # ['abb']
print(re.findall("ab{2,6}","abbb")) # ['abbb'] (一个 a,2~6 个 b)
print(re.findall("ab{1,}","abbb")) # ['abbb'] (相当于 ab+)
print(re.findall("ab{0,}","abbb")) # ['abbb'] (相当于 ab*)
[]
: 放一组字符, 逐个匹配
print(re.findall("a[*1_c-]b","a*ba1b a_baaba-b")) # ['a*b', 'a1b', 'a_b', 'a-b']
print(re.findall("a[^*1c-]b","a*ba1b a_baab")) # ['a_b', 'aab'] (匹配非 [] 内的字符)
print(re.findall("a[0-9]b","a*ba1b a3baa2b")) # ['a1b', 'a3b', 'a2b'] (0~9)
print(re.findall("a[a-z]b","a*banb apbaaob")) # ['anb', 'apb', 'aob'] (a~z)
print(re.findall("a[a-zA-Z0-9]b","a*banb aPbaa7b")) # ['anb', 'aPb', 'a7b']
print(re.findall("a[^a-zA-Z0-9]b","a*banb aPbaa7b")) # ['a*b'] (非 a~z,A~Z,0~9)
()
: 分组
print(re.findall('ab+','ababab123'))
# ['ab', 'ab', 'ab']
print(re.findall('(ab)+123','ababab123'))
# ['ab'],匹配到末尾的 ab123 中的 ab
print(re.findall('(?:ab)+123','ababab123'))
# findall 的结果不是匹配的全部内容,而是组内的内容, ?: 可以让结果为匹配的全部内容
print(re.findall('href="(.*?)"','<a href="http://www.baidu.com"> 点击 </a>'))
# ['http://www.baidu.com']
print(re.findall('href="(?:.*?)"','<a href="http://www.baidu.com"> 点击 </a><a href="http://www.aiqiyi.com"> 点击 2 </a>'))
# ['href="http://www.baidu.com"', 'href="http://www.aiqiyi.com"']
a|b
: 左右两边都匹配
print(re.findall("shawn|song","shawn is man song is shawn")) # ['shawn', 'song', 'shawn'] (不加括号全局匹配)
print(re.findall("A(?:abc|cba)A","AabcA")) # ['AabcA']
print(re.findall("com(?:puter|e)","come on! Here is a computer ")) # ['come', 'computer']
\
: 转义字符
# 转义字符,使后一个字符改变原来的意思。# 如果字符串中有字符 * 需要匹配,可以使用 \* 或者字符集 [*]
print(re.findall("a.b","a1b a.b")) # ['a1b', 'a.b']
print(re.findall("a\.b","a1b a.b")) # ['a.b']
💠匹配所有数字类型示例
print(re.findall("\d+\.?\d*","12as3.45qw2k7")) # ['12', '3.45', '2', '7']
💠.*
贪婪匹配示例
- 匹配更多的字符
print(re.findall("a.*b","a11b22222b33")) # ['a11b22222b']
💠.*?
非贪婪匹配示例
- 尽可能少的匹配
print(re.findall("a.*?b","a11b22222b3")) # ['a11b']
六. re 模块常用方法示例
1、re.findall(pattern,string)
- 返回所有满足匹配的结果, 按照顺序依次放入列表中, 如果没有匹配结果, 返回一个空列表
print(re.findall("abc","112abc333")) # ['abc']
print(re.findall("abcd","12abc333")) # []
2、re.search(pattern,string)
- 匹配整个字符串, 只到找到 第一个 匹配然后返回一个包含匹配信息的对象(re.Match 对象)
- 该对象可以通过调用 group() 方法得到匹配的字符串, 如果字符串没有匹配,则返回None
- 如果没有匹配到值就调用 group() 方法, 抛出异常
print(re.search("abc","112abc333abc"))
# <re.Match object; span=(3, 6), match='abc'>
print(re.search("abc","112abc333abc").group()) # abc
print(re.search("abcd","12abc333abc")) # None
print(re.search("abcd","12abc333abc").group())
# 报错 "AttributeError" (因为没拿到这个对象, 所以没有 group()属性
3、re.match(pattern,string)
- 与 re.search 功能相同, 但必须匹配起始位置, 不然返回 None
- 可以使用上尖号 ^ : re.search("^abc","abc") 实现 re.match() 功能
print(re.match("abc","abc22abc")) # <re.Match object; span=(0, 3), match='abc'>
print(re.match("abc","abc22abc").group()) # abc
print(re.match("abc","1abc22abc")) # None
print(re.search("^abc","1abcabc")) # None (re.search)
4、re.split(pattern,string)
- 以 规定字符 作为分隔符对字符串进行切分, 切分结果放入列表, 没匹配到返回原字符串列表
- 将规定字符放入
[]
则是逐个匹配
print(re.split("o","abcoabcoabc")) # ['abc', 'abc', 'abc']
print(re.split("ab","abcoabcoabc")) # ['','co','co','c']
print(re.split("d","abcoabcoabc")) # ['abcoabcoabc'] (没匹配到返回原字符串)
print(re.split("[o]","abcoabcoabc")) # ['abc', 'abc', 'abc']
print(re.split("[ob]","abcoabcoabc")) # ['a', 'c', 'a', 'c', 'a', 'c']
print(re.split("[oba]","abcoabcoabc")) # ['','', 'c', '','', 'c', '','', 'c']
# 先使用 "o" 匹配进行分隔, 得到结果 "['abc', 'abc', 'abc']", 在使用 "b" 进行匹配分隔, 得到结果后再使用 "a" 匹配进行分隔
5、re.sub() 和 re.subn()
- 匹配"规则字符", 并将其改成指定字符, 返回改变后的字符串, 后面可跟数量参数, 不指定默认替换所有
- re.subn() 返回一个元组, 第二个元素返回的是替换的个数
print(re.sub("a","A","i am a man")) # i Am A mAn
print(re.sub("a","b","i am a man")) # i bm b mbn
print(re.sub("am","b","i am a man")) # i b a man
print(re.sub("a","AA","i am a man")) # i AAm AA mAAn
print(re.sub("a","A","i am a man",1)) # i Am a man
print(re.sub("a","A","i am a man",2)) # i Am A man
print(re.sub("a","A","i am a man",3)) # i Am A mAn
print(re.sub("a","A","i am a man",9)) # i Am A mAn (超出字符串中最大的个数不会报错, 而是替换所有)
print(re.subn("a","A","i am a man")) # ('i Am A mAn', 3) (显示替换的个数)
print(re.subn("a","A","i am a man",2)) # ('i Am A man', 2)
6、re.compile()
- 在该函数中传入"规则字符", 返回一个对象, 它单独使用没有任何意义, 需要和 findall(), search(), match() 组合使用
obj = re.compile("\d{2}")
print(obj) # re.compile('\\d{2}')
print(obj.findall("ab12c33b44")) # ['12', '33', '44']
print(obj.search("ab12c33b44").group()) # 12
print(obj.match("12c33b44").group()) # 12 (注意, 必须在开头就匹配到, 不然 group 会报错)
七. 补充非捕获模式 : ?: ?! ?=
字符 | 描述 |
---|---|
(?:pattern) | 匹配 pattern,但不捕获匹配结果。 |
(?=pattern) | 零宽度正向预查,不捕获匹配结果。 |
(?!pattern) | 零宽度负向预查,不捕获匹配结果。 |
(?<=pattern) | 零宽度正向回查,不捕获匹配结果。 |
(?<!pattern) | 零宽度负向回查,不捕获匹配结果。 |
1、?! 示例
?! pattern
, 表示在没有配到 pattern 的字符串的前提下,再进行后续的正则表达式匹配,后续匹配仍然从被匹配字符串的头开始
🍓 匹配密码,密码必须是由 6 位数字与字母组成,并且不能是纯数字也不能是纯字母
while 1:
passwd = input(">>").strip()
res = re.match("(?![\d]+$)(?![a-zA-Z]+$)[\da-zA-Z]{6}$",passwd)
if res:
print(f" 密码:{res.group()}")
else:
print(" 密码不符合规则 ")
2、?= 示例
?= pattern
, 表示在配到 pattern 的字符串的前提下,再进行后续的正则表达式匹配,后续匹配仍然从被匹配字符串的头开始
🍓 匹配密码,必须包含大写,小写和数字, 和特殊字符(!,@,#,%,&), 且大于 6 位
while 1:
passwd = input(" 请输入密码 >>").strip()
res = re.search("(?=.*[\d])(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#%&])^([\da-zA-Z!@#%&]{7,})$",passwd)
if res:
print(res.group())
else:
print(" 密码强度不够 ")
正文完